package net.comm.encrypt;

/*
 * @(#)RSA.java    2006-5-18
 *
 * Copyright (c) 2006 Seadn All rights reserved.
 */


import java.math.BigInteger;
import java.security.SecureRandom;

/*******************************************************************************
 * RSA 类实现了定义在RFC 2313中的RSA 算法。
 ******************************************************************************/

/**
 * seadn.common.serurity.RSA
 * 
 * @Title: RSA
 * 
 * @Description:
 * 
 * RSA算法概述：
 * 
 * RSA算法非常简单，概述如下：
 * 找两素数p和q
 * 取n=p*q
 * 取t=(p-1)*(q-1)
 * 取任何一个数e,要求满足e<t并且e与t互素（就是最大公因数为1）
 * 取d*e%t==1

 * 这样最终得到三个数： n   d   e

 * 设消息为数M (M <n)
 * 设c=(M**d)%n就得到了加密后的消息c 
 * 设m=(c**e)%n则 m == M，从而完成对c的解密。
 * 注：**表示次方,上面两式中的d和e可以互换。

 * 在对称加密中：
 * n d两个数构成公钥，可以告诉别人；
 * n e两个数构成私钥，e自己保留，不让任何人知道。
 * 给别人发送的信息使用e加密，只要别人能用d解开就证明信息是由你发送的，构成了签名机制。
 * 别人给你发送信息时使用d加密，这样只有拥有e的你能够对其解密。

 * rsa的安全性在于对于一个大数n，没有有效的方法能够将其分解
 * 从而在已知n d的情况下无法获得e；同样在已知n e的情况下无法
 * 求得d。
 * 
 * @author Seadn
 * @version 1.0, 2006-5-18
 * @created 2006-5-18 17:07:01
 * @modify <br>
 *         2006-5-18 17:07:01 Seadn Created
 * @see RFC 2313
 * @since JDK 1.4
 * 
 */
public class RSA {
    private final static BigInteger one = BigInteger.ONE;

    private final static SecureRandom random = new SecureRandom();

    private final static int defaultBitLength = 128;

    private BigInteger privateKey = null;// 私钥

    private BigInteger publicKey = null;// 公钥

    private BigInteger modulus = null;// n

    private BigInteger p1 = null; // 原始质数p

    private BigInteger p2 = null; // 原始质数q

    private BigInteger modulusE; // 实际模数n

    private BigInteger publicKeyE; // 实际公钥

    private int N;// 位数

    private BigInteger phi0;

    public RSA() {
        this(defaultBitLength);
    }

    /**
     * 
     * Created constructor
     * 
     * @param N
     * @created 2006-5-18 19:33:02
     */
    public RSA(int N) {
        this.N = N;
        // 生成N位公钥和私钥
        p1 = BigInteger.probablePrime(N / 2, random);
        p2 = BigInteger.probablePrime(N / 2, random);
        phi0 = (p1.subtract(one)).multiply(p2.subtract(one));
        modulus = p1.multiply(p2);
        setPrivateKey();
        publicKey = privateKey.modInverse(phi0);

        modulusE = modulus;
        publicKeyE = publicKey;
    }

    /**
     * 
     * @param publicKeyE0
     * @param modulusE0
     * @created 2006-5-18 19:33:10
     */
    public void setPublicKey(BigInteger publicKeyE0, BigInteger modulusE0) {
        publicKeyE = publicKeyE0;
        modulusE = modulusE0;
    }

    /**
     * 
     * @param message
     * @return
     * @created 2006-5-18 19:33:12
     */
    public String encrypt(String message) {
        BigInteger exp = new BigInteger(message.getBytes());
        BigInteger enc = encrypt(exp);
        return enc.toString();
    }

    /**
     * 
     * @param message
     * @return
     * @created 2006-5-18 19:33:15
     */
    public BigInteger encrypt(BigInteger message) {
        BigInteger rep = null;
        String str_message = new String(message.toByteArray());
        if (message != null) {
            if (str_message.length() <= (N / 8))
                if (publicKeyE != null && modulusE != null
                        && message.toByteArray().length < Integer.MAX_VALUE)
                    rep = message.modPow(publicKeyE, modulusE);
        }
        return rep;
    }

    /**
     * 
     * @param message
     * @param publicKeyP
     * @param modulusP
     * @return
     * @created 2006-5-18 19:33:17
     */
    public String encrypt(String message, BigInteger publicKeyP,
            BigInteger modulusP) {
        BigInteger exp = new BigInteger(message.getBytes());
        BigInteger enc = encrypt(exp, publicKeyP, modulusP);
        return new String(enc.toByteArray());
    }

    /**
     * 
     * @param message
     * @param publicKeyP
     * @param modulusP
     * @return
     * @created 2006-5-18 19:33:19
     */
    public BigInteger encrypt(BigInteger message, BigInteger publicKeyP,
            BigInteger modulusP) {
        BigInteger rep = null;
        String str_message = new String(message.toByteArray());

        if (str_message.length() <= (N / 8))
            if (publicKeyP != null && modulusP != null
                    && message.toByteArray().length < Integer.MAX_VALUE)
                rep = message.modPow(publicKeyP, modulusP);

        return rep;
    }

    /**
     * 
     * @param message
     * @return
     * @created 2006-5-18 19:33:23
     */
    public String decrypt(String message) {
        BigInteger exp = new BigInteger(message);
        BigInteger enc = decrypt(exp);
        return new String(enc.toByteArray());
    }

    /**
     * 
     * @param encrypted
     * @return
     * @created 2006-5-18 19:33:26
     */
    public BigInteger decrypt(BigInteger encrypted) {
        return encrypted.modPow(privateKey, modulus);
    }

    /**
     * 
     * 
     * @created 2006-5-18 17:29:59
     */
    private void setPrivateKey() {
        /*
         * 设置privateKey，满足： 1. privateKey和phi0的最大公约数为1(互质）。 2.
         * privateKey小于modulus 3. privateKey不小于p1和p2的最大者
         */
        do {
            privateKey = BigInteger.probablePrime(N / 2, random);
        } while (privateKey.gcd(phi0).intValue() != 1
                || privateKey.compareTo(modulus) != -1
                || privateKey.compareTo(p1.max(p2)) == -1);
    }

    /**
     * 
     */
    public String toString() {
        String s = "";
        s += "public  = " + publicKey + "\n";
        s += "modulus = " + modulus + "\n";

        return s;
    }

    /**
     * The getter method of the field modulus.
     * 
     * @return Returns the modulus
     * @created 2006-5-18 17:56:45
     */
    public BigInteger getModulus() {
        return this.modulus;
    }

    /**
     * The getter method of the field publicKey.
     * 
     * @return Returns the publicKey
     * @created 2006-5-18 17:56:45
     */
    public BigInteger getPublicKey() {
        return this.publicKey;
    }
}

